#include "stdafx.h"
#include "System.h"
#include "Application.h"
#include <QtCore/QFile>

// define memory dumping when using Visual Studio
#ifdef _MSC_VER
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#define new new(1, __FILE__, __LINE__)
#define MEMDUMP _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

namespace ham
{
	System* System::m_instance = 0;

	System::System(int argc, char *argv[])
		: m_application(argc, argv),
		m_mainWindow(0)
	{
	}

	System::~System(void)
	{
		// delete all registered modules
		for (int i = 0; i < m_modules.size(); i++) {
			Module* mod = m_modules[i];
			mod->saveProperties();
			delete mod;
		}
		m_modules.clear();
	}

	void System::loadState()
	{
		QString filename = "layout.bin";
		QFile file(filename);

		if (file.open(QFile::ReadOnly)) {
			uchar moduleCount = 0;
			file.getChar((char*)&moduleCount);

			for (int i = 0; i < moduleCount; i++) {
				// read module name
				uchar moduleNameSize = 0;
				file.getChar((char*)&moduleNameSize);
				QString moduleName(file.read(moduleNameSize));
				std::string name = moduleName.toStdString();

				// read state information
				uchar geometrySize = 0;
				file.getChar((char*)&geometrySize);
				QByteArray geometry = file.read(geometrySize);

				uchar stateSize = 0;
				file.getChar((char*)&stateSize);
				QByteArray state = file.read(stateSize);
				
				// save state information for later use, when creating module instances
				QPair<QByteArray, QByteArray> stateInfo(geometry, state);

				if (m_stateInfo.find(moduleName) != m_stateInfo.end()) {
					// Warning: there are more modules with the same name
				}

				m_stateInfo[moduleName] = stateInfo;
			}
		}
	}

	void System::saveState()
	{
		QString filename = "layout.bin";	// UNDONE: filename?
		QFile file(filename);

		if (file.open(QFile::WriteOnly)) {
			
			file.putChar(m_modules.size());

			// save geometry and state information for each module
			for (int i = 0; i < m_modules.size(); i++) {
				Module* module = m_modules[i];
			
				// write module name
				file.putChar(module->getModuleName().toLocal8Bit().size());
				file.write(module->getModuleName().toLocal8Bit());
				
				// get state information
				QByteArray geometry = module->saveGeometry();
				QByteArray state = module->saveState();

				// write state information
				file.putChar(geometry.size());
				file.write(geometry);
				file.putChar(state.size());
				file.write(state);
			}
		}
	}
	
	QVector<Module*>& System::getModules()
	{
		return m_modules;
	}

	bool System::removeModule(Module* module)
	{
		for (QVector<Module*>::iterator it = m_modules.begin(); it != m_modules.end(); it++) {
			if (*it == module) {
				it = m_modules.erase(it);
				return true;
			}
			else
				it++;
		}
		return false;
	}
	
	bool System::initModule(Module* module)
	{
		// init module
		if (!module->loadProperties() ||
			!module->init(this))
			return false;
		
		if (!m_mainWindow)
			return false;

		// add menu entries
		QList<QMenu*> menus = module->getMenus(m_mainWindow);
		for (QList<QMenu*>::const_iterator it = menus.begin();
			it != menus.end(); it++)
			m_mainWindow->addMenu(*it);
		
		// add main widgets
		QList<QPair<QString, QWidget*>> mainWidgets = module->getMainWidgets(m_mainWindow);
		for (QList<QPair<QString, QWidget*>>::const_iterator it = mainWidgets.begin();
			it != mainWidgets.end(); it++)
			m_mainWindow->addMainWidget(it->second, it->first);

		// add toolbar widgets
		QList<QPair<QString, QWidget*>> toolbarWidgets = module->getToolbarWidgets(m_mainWindow);
		for (QList<QPair<QString, QWidget*>>::const_iterator it = toolbarWidgets.begin();
			it != toolbarWidgets.end(); it++)
			m_mainWindow->addToolbarWidget(it->second, it->first);

		// restore module state if possible
		if (m_stateInfo.find(module->getModuleName()) != m_stateInfo.end()) {
			const QPair<QByteArray, QByteArray>& stateInfo = m_stateInfo[module->getModuleName()];
			module->restoreGeometry(stateInfo.first);
			module->restoreState(stateInfo.second);
		}

		// check if module is correctly initialized
		if (!module->isInit())
			return false;
		
		return true;
	}
	
	bool System::initModules()
	{
		for (int i = 0; i < m_modules.size(); i++) {
			Module* mod = m_modules[i];
			if (!initModule(mod)) {
				removeModule(mod);
				delete mod;
				return false;
			}
		}

		return true;
	}

	System* System::init(int argc, char *argv[])
	{
		if (!m_instance) {
			m_instance = new System(argc, argv);
			if (!m_instance->init()) {
				delete m_instance;
				m_instance = 0;
				return 0;
			}
		}

		return m_instance;
	}

	bool System::init()
	{
		if (m_mainWindow)
			return true;

		loadState();
		
		// register main application
		m_mainWindow = registerModule<Application>();
		if (!m_mainWindow)
			return false;

		return true;
	}

	int System::run()
	{
		try {
			if (!m_mainWindow)
				return 1;

			// init modules
			if (!initModules())
				return 1;
			
			// show and process main application
			m_mainWindow->show();
			int retVal = m_application.exec();
			m_mainWindow->saveProperties();

			saveState();

			return retVal;
		}
		catch (Exception& e) {
			e.show();
		}

#ifdef _MSC_VER
		//MEMDUMP;
#endif

		return 1;
	}
}